/*
*
*
*     文档创建：jjl, jjl@hzncc.com
*
*       常用模块的收集
*/

/*#########################################################
bin2gray: 二进制转格雷码
WIDTH: 转换数据的宽度
例子:
bin2gray #(
  .WIDTH( 32 )
) BG1 (
  .bin_in(  ),
  .gray_out(  )
);
#########################################################*/
module bin2gray #( 
parameter WIDTH = 32
)(
  input [WIDTH-1:0] bin_in,
  output logic[WIDTH-1:0] gray_out
);

always_comb begin
  gray_out[WIDTH-1:0] = bin_in[WIDTH-1:0]^(bin_in[WIDTH-1:0]>>1);
end

endmodule

/*#########################################################
gray2bin:格雷码到二进制转换
WIDTH: 转换数据的宽度
例子:
gray2bin #(
  .WIDTH( 32 )
) GB1 (
  .gray_in(  ),
  .bin_out(  )
);
#########################################################*/
module gray2bin #(
parameter WIDTH = 32
)(
   input [WIDTH-1:0] gray_in,
   output [WIDTH-1:0] bin_out
);

genvar i;
generate
   for( i=0; i<WIDTH; i++ ) begin
      assign bin_out[i] = ^gray_in[WIDTH-1:i];
   end
endgenerate

endmodule

/*#########################################################
bin2pos:位置译码，常用3-8译码，2-4译码，4-16译码
BIN_WIDTH: 转换数据的宽度
bin2pos #(
  .BIN_WIDTH( 8 )
) BP1 (
  .bin(  ),
  .pos(  )
);

#########################################################*/
module bin2pos #( 
parameter BIN_WIDTH = 8,
  POS_WIDTH = 2**BIN_WIDTH
)(
  input [(BIN_WIDTH-1):0] bin,
  output logic [(POS_WIDTH-1):0] pos
);


always_comb begin
  pos = 0;
  pos[bin] = 1'b1;
end

endmodule

/*#########################################################
pos2bin:位置到二进制转换，如4'b0001->2'd0, 4'b0010->2'd01
256'b10100000 becomes 8'd5
BIN_WIDTH: 转换数据的宽度
pos2bin #(
  .BIN_WIDTH( 8 )
) PB1 (
  .pos(  ),
  .bin(  ),

  .err_no_hot(  ),
  .err_multi_hot(  )
);
#########################################################*/
module pos2bin #( 
parameter BIN_WIDTH = 8,
  POS_WIDTH = 2**BIN_WIDTH
)(
  input [(POS_WIDTH-1):0] pos,
  output logic [(BIN_WIDTH-1):0] bin,

// error flags
  output logic err_no_hot,    // no active bits in pos[] vector
  output logic err_multi_hot  // multiple active bits in pos[] vector
                              // only least-sensitive active bit affects the output
);


assign err_no_hot = (pos[(POS_WIDTH-1):0] == 0);

integer i;

logic found_hot;
always_comb begin
  err_multi_hot=0;
  bin[(BIN_WIDTH-1):0]=0;
  found_hot = 0;
  for (i = 0; i < POS_WIDTH ; i++) begin

    if ( ~found_hot && pos[i] ) begin
      bin[(BIN_WIDTH-1):0] = i;
    end

    if ( found_hot && pos[i] ) begin
      err_multi_hot=1'b1;
    end

    if ( pos[i] ) begin
      found_hot = 1'b1;
    end

  end // for
end // always_comb

endmodule

/*#########################################################
clk_divider:分频器，从二分频到2^WIDTH分频
clk_divider #(
  .WIDTH( 32 )
) CD1 (
  .clk( clk ),
  .nrst( 1'b1 ),
  .ena( 1'b1 ),
  .out(  )
);
#########################################################*/
module clk_divider #( 
parameter WIDTH = 32
)(
  input clk,
  input nrst,
  input ena,
  output logic [(WIDTH-1):0] out = 0
);


always_ff @(posedge clk) begin
  if ( ~nrst ) begin
    out[(WIDTH-1):0] <= 0;
  end else if (ena) begin
    out[(WIDTH-1):0] <= out[(WIDTH-1):0] + 1'b1;
  end
end

endmodule

/*#########################################################
edge_detect:信号边沿检测，可检测上升沿，下降沿，双沿
REGISTER_OUTPUTS：1'b0:直接组合逻辑输出，1'b1:经过寄存器输出
#########################################################*/
module edge_detect #( 
parameter bit [0:0] REGISTER_OUTPUTS = 1'b0    // 0 - comb. implementation (default)
                                       // 1 - registered implementation
)(
  input clk,
  input nrst,

  input in,
  output logic rising,
  output logic falling,
  output logic both
);

// data delay line
logic in_d = 0;
always_ff @(posedge clk) begin
  if ( ~nrst ) begin
    in_d <= 0;
  end else begin
    in_d <= in;
  end
end

logic rising_comb;
logic falling_comb;
logic both_comb;
always_comb begin
  rising_comb = nrst && (in && ~in_d);
  falling_comb = nrst && (~in && in_d);
  both_comb = nrst && (rising_comb || falling_comb);
end

generate
  if( REGISTER_OUTPUTS=='0 ) begin

    // combinational outputs, no delay
    always_comb begin
      rising = rising_comb;
      falling = falling_comb;
      both = both_comb;
    end // always

  end else begin

    // registered outputs, 1 cycle delay
    always_ff @(posedge clk) begin
      if( ~nrst ) begin
        rising <= 0;
        falling <= 0;
        both <= 0;
      end else begin
        rising <= rising_comb;
        falling <= falling_comb;
        both <= both_comb;
      end // always
    end // if

  end // end else
endgenerate

endmodule

/*#########################################################
leave_one_hot:保留输入二进制最低位的1,如：
    16'b1101_0000 -> 8'b0001_0000
    16'b1101_0010 -> 8'b0000_0010

leave_one_hot #(
  .WIDTH( 32 )
) OH1 (
  .in(  ),
  .out(  )
);
#########################################################*/
module leave_one_hot #( 
parameter WIDTH = 32
)(
  input [WIDTH-1:0] in,
  output logic [WIDTH-1:0] out
);


genvar i;
generate
  for( i=1; i<WIDTH; i++ ) begin : gen_for

    always_comb begin
        out[i] <= in[i] && ~( |in[(i-1):0] );
    end

  end  // for i
endgenerate

assign out[0] = in[0];

endmodule
